The inspection of process state in a debugger has traditionally used one of two approaches: out-of-process inspection and in-process inspection. In out-of-process inspection, the debugger obtains the values of variables by reading debuggee memory or by consuming debuggee context. For in-process inspection, the debugger evaluates properties by actually executing property functions within the debuggee process. This in-process inspection is called function evaluation or “func-eval” in debugger parlance. Both approaches have advantages and disadvantages.
In an out-of-process inspection model, the debugger cannot corrupt the state of the debuggee. The developer sees a raw view of the debuggee state, which tends to be at a lower level than the abstraction the API designer intended. This is because the debugger can only obtain the backing values of a property if they are accessible in debuggee memory. For instance, if a property's value is calculated, the debugger can only show the raw variables used in the calculation.
Properties with values that depend on state outside the debuggee's memory, such as state shared with other processes (either in memory or on a storage media), state from some other connected device, removable storage, etc., cannot be read in this manner. Other state-dependent values that cannot be read in this way are states in the operating system kernel or cross-machine implemented states. The out-of-process model requires the developer to reverse engineer the implementation of the API abstraction from the values available as raw variables, which can be difficult, confusing, or impossible to do.
For the in-process or func-eval model, the developer sees the exact view of the abstraction the API designer intended. No mapping from implementation to public view is necessary. However, in a func-eval model, any side effects for the implementation of the property will affect debuggee state, which may lead to developer confusion and incorrect debuggee behavior. The debuggee may not be in a state where code can be executed, such as highly stressed processes that are near out-of-memory situations or threads within the process that have entered the kernel. In this state, debugger inspection is impossible. Executing a function-evaluation can lead to debuggee deadlock or corruption. Specifically if the implementation of a property depends other threads executing. For instance, if a property tries to take a lock held by another thread, that property cannot execute unless the thread that holds the lock releases it, leading to deadlock.
Furthermore, in the proxy/stub model used by some distributed environments, the call may require multiple threads to execute in order to enable another thread to “pump” or handle an incoming call from another thread that is doing a func-eval. Allowing the other threads in the process to run (i.e. “slipping the threads”) is something the debugger generally cannot allow because the actual execution point for every thread would change on each func-eval. Such cross-context calls can lead to un-recoverable corruption of the debuggee if they do not complete correctly.